#ifndef CNC_BASE_MILL_KERNEL_H
#define CNC_BASE_MILL_KERNEL_H

#include <bcm/bcm.h>
#include "opmodeid.h"
#include "setupmode.h"
#include "mdimode.h"
#include "jogmode.h"
#include "rapidmode.h"
#include "hwmode.h"
#include "memmode.h"
#include "tapemode.h"
#include "editmode.h"
// used interface
#include "kernelsetup.h"
// provided interface
#include "taskctl.h"
// required interfaces
#include "uiservs.h"
#include "keyboard.h"
#include "machop.h"
#include "vkeyboard.h"

namespace cnc {

	namespace base {

		struct MillKernel : public bcm::Identifier<bcm::Component>
		{
			bcm::GetComponentResult result_;
			bcm::Component *cncKernel_;
			KernelSetup *krnlSetup_; // move to constructor?
			TaskControl *krnlCtl_;
			SetupMode setupMode_;
			MDIMode mdiMode_;
			JogMode jogMode_;
			RapidMode rapidMode_;
			HandwheelMode handwheelMode_;
			MemoryMode memoryMode_;
			TapeMode tapeMode_;
			EditMode editMode_;
//			GUITask guiTask_;
		protected:
			MillKernel() : bcm::Identifier<bcm::Component>("millkrnl", "CNC Milling Machine Kernel"),
				cncKernel_(0),
				result_(bcm::GC_OK)
			{
				krnlSetup_ = 0;
				krnlCtl_ = 0;
				bcm::ComponentManager &compMgr = bcm::ComponentManager::instance();
				switch (compMgr.loadComponent(TEXT("cnckernel.dll"))) {
				case bcm::LC_NODLL:
				case bcm::LC_NOCOMPONENT:
				case bcm::LC_ALREADYUSED:
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
//				bcm::Component *component;
				switch (compMgr.getComponent("cnckrnl", &cncKernel_)) {
				case bcm::GC_UNREGISTERED:
				case bcm::GC_NOCOMPONENT:
				case bcm::GC_INCOMPLETE:
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
				bcm::Interface *iface;
				iface = cncKernel_->getInterface("krnlsetup");
				if (iface == 0) {
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
				krnlSetup_ = dynamic_cast<KernelSetup *>(iface);
				if (krnlSetup_ == 0) {
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
//				krnlSetup_->addTask(&guiTask_);
				krnlSetup_->addMode(cnc::base::OPMODE_SETUP, &setupMode_);
				krnlSetup_->addMode(cnc::base::OPMODE_MDI, &mdiMode_);
				krnlSetup_->addMode(cnc::base::OPMODE_JOG, &jogMode_);
				krnlSetup_->addMode(cnc::base::OPMODE_RAPID, &rapidMode_);
				krnlSetup_->addMode(cnc::base::OPMODE_HANDWHEEL, &handwheelMode_);
				krnlSetup_->addMode(cnc::base::OPMODE_MEMORY, &memoryMode_);
				krnlSetup_->addMode(cnc::base::OPMODE_TAPE, &tapeMode_);
				krnlSetup_->addMode(cnc::base::OPMODE_EDIT, &editMode_);
				setupMode_.setCoordinator(krnlSetup_);
				mdiMode_.setCoordinator(krnlSetup_);
				jogMode_.setCoordinator(krnlSetup_);
				rapidMode_.setCoordinator(krnlSetup_);
				handwheelMode_.setCoordinator(krnlSetup_);
				memoryMode_.setCoordinator(krnlSetup_);
				tapeMode_.setCoordinator(krnlSetup_);
				editMode_.setCoordinator(krnlSetup_);
				krnlSetup_->selectMode(cnc::base::OPMODE_SETUP);
				iface = cncKernel_->getInterface("taskctl");
				if (iface == 0) {
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
				krnlCtl_ = dynamic_cast<TaskControl *>(iface);
				if (krnlCtl_ == 0) {
					result_ = bcm::GC_INCOMPLETE;
					return;
				}
			}
		protected: // bcm::Component
			virtual bcm::IdentifierData *getProvidedInterfaceTable()
			{
				static bcm::IdentifierData idData[] = {
					{ "taskctl", "Task Control" },
					{ 0, 0 }
				};
				return idData;
			}
			virtual bcm::IdentifierData *getRequiredInterfaceTable()
			{
				static bcm::IdentifierData idData[] = {
					{ "taskctl", "Task Control" },
					{ "uiservs", "User Interface Services" },
					{ "machop", "Machine Operation" },
					{ "kbd", "Keyboard" },
					{ 0, 0 }
				};
				return idData;
			}
			virtual bcm::Interface *getInterface(const char *id)
			{
				if (std::string("taskctl") == id)
					return krnlCtl_;
				return 0;
			}
			virtual bool setInterface(bcm::Interface *iface)
			{
				if (iface == 0)
					return false;
				if (std::string("taskctl") == iface->getID()) {
					cncKernel_->setInterface(iface);
				} else if (std::string("uiservs") == iface->getID()) {
					cnc::ui::Services *uiServices = dynamic_cast<cnc::ui::Services *>(iface);
					if (uiServices == 0)
						return false;
					setupMode_.setUIServices(uiServices);
					mdiMode_.setUIServices(uiServices);
					jogMode_.setUIServices(uiServices);
					rapidMode_.setUIServices(uiServices);
					handwheelMode_.setUIServices(uiServices);
					memoryMode_.setUIServices(uiServices);
					tapeMode_.setUIServices(uiServices);
					editMode_.setUIServices(uiServices);
//					guiTask_.setUIServices(uiServices);
				} else if (std::string("machop") == iface->getID()) {
					cnc::base::MachineOperation *machOp = dynamic_cast<cnc::base::MachineOperation *>(iface);
					if (machOp == 0)
						return false;
					setupMode_.setMachineOperation(machOp);
					mdiMode_.setMachineOperation(machOp);
					jogMode_.setMachineOperation(machOp);
					rapidMode_.setMachineOperation(machOp);
					handwheelMode_.setMachineOperation(machOp);
					memoryMode_.setMachineOperation(machOp);
					tapeMode_.setMachineOperation(machOp);
					editMode_.setMachineOperation(machOp);
				} else if (std::string("kbd") == iface->getID()) {
					cnc::comm::Keyboard *keyboard = dynamic_cast<cnc::comm::Keyboard *>(iface);
					if (keyboard == 0) {
						//std::cout << "Keyboard failure!" << std::endl;
						return false;
					}
					setupMode_.setKeyboard(keyboard);
					mdiMode_.setKeyboard(keyboard);
					jogMode_.setKeyboard(keyboard);
					rapidMode_.setKeyboard(keyboard);
					handwheelMode_.setKeyboard(keyboard);
					memoryMode_.setKeyboard(keyboard);
					tapeMode_.setKeyboard(keyboard);
					editMode_.setKeyboard(keyboard);
				} else
					return false;
				return true;
			}
		public:
			static MillKernel &instance(bcm::GetComponentResult *result)
			{
				static MillKernel object;
				if (result != 0)
					*result = object.result_;
				return object;
			}
		};

//		extern "C" MillKernel *getMillKernel();

	} // namespace base

} // namespace cnc

#endif

